package com.github.glomadrian.roadrunner.painter.indeterminate;

import com.github.glomadrian.roadrunner.painter.RoadRunnerPainter;
import com.github.glomadrian.roadrunner.painter.configuration.Direction;
import com.github.glomadrian.roadrunner.painter.configuration.indeterminate.MaterialPainterConfiguration;
import com.github.glomadrian.roadrunner.path.PathContainer;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;

/**
 * @author Adrián García Lomas
 */
public class MaterialPainter extends RoadRunnerPainter implements IndeterminatePathPainter {

    private static final String TAG = "MaterialPainter";
    private AnimatorValue movementAnimator;
    private AnimatorValue frontValueAnimator;
    private AnimatorValue backValueAnimator;
    private int movementLoopTime = 3000;
    private float movementLineSize = 0.07f;
    //Front
    private int frontOffset = 0;
    private float sideIncrementSize = 0.7f;
    private int sideAnimationTime = 600;
    private int sideStartDelay = 200;
    //Back
    private int backOffset = 0;
    //Move
    private float currentMoveValue;
    private float endMoveValue;

    /**
     * MaterialPainter
     *
     * @param pathData
     * @param view
     * @param pathPainterConfiguration
     */
    public MaterialPainter(PathContainer pathData, Component view,
                         MaterialPainterConfiguration pathPainterConfiguration) {
        super(pathData, view);
        initConfiguration(pathPainterConfiguration);
        init();
    }

    private void initConfiguration(MaterialPainterConfiguration pathPainterConfiguration) {
        movementDirection = pathPainterConfiguration.getMovementDirection();
        color = pathPainterConfiguration.getColor();
        strokeWidth = pathPainterConfiguration.getStrokeWidth();
    }

    private void init() {
        initPaint();
        initLineMovement();
        initMovementAnimator();
        initFrontValueAnimator();
        initBackValueAnimator();
    }

    /**
     * initPaint
     */
    public void initPaint() {
        paint = new Paint();
        paint.setColor(new Color(color));
        paint.setStrokeWidth(strokeWidth);
    }

    private void initLineMovement() {
        movementLinePoints = getNumberOfLinePointsInRange(movementLineSize);
    }

    private void initMovementAnimator() {
        movementAnimator = new AnimatorValue();
        movementAnimator.setDuration(movementLoopTime);
        movementAnimator.setCurveType(Animator.CurveType.LINEAR);
        movementAnimator.setLoopedCount(AnimatorValue.INFINITE);
        movementAnimator.setValueUpdateListener(new MovementLineAnimatorUpdateListener());
        movementAnimator.setLoopedListener(animator -> {
            resetMoveValue();
            movementAnimator.stop();
            movementAnimator.setDuration(movementLoopTime);
            movementAnimator.start();
        });
        resetMoveValue();
    }

    private void resetMoveValue(){
        if (movementDirection.equals(Direction.CLOCKWISE)) {
            currentMoveValue = 0;
            endMoveValue = 1;
        } else {
            currentMoveValue = 1;
            endMoveValue = 0;
        }
    }

    private void initFrontValueAnimator() {
        frontValueAnimator = new AnimatorValue();
        frontValueAnimator.setDuration(sideAnimationTime);
        frontValueAnimator.setDelay(sideStartDelay);
        frontValueAnimator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        frontValueAnimator.setStateChangedListener(new FrontOffsetAnimatorListener());
        frontValueAnimator.setValueUpdateListener(new FrontOffsetAnimatorUpdateListener());
    }

    private void initBackValueAnimator() {
        backValueAnimator = new AnimatorValue();
        backValueAnimator.setDuration(sideAnimationTime);
        backValueAnimator.setDelay(sideStartDelay);
        backValueAnimator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        backValueAnimator.setStateChangedListener(new BackOffsetAnimatorListener());
        backValueAnimator.setValueUpdateListener(new BackOffsetAnimatorUpdateListener());
    }

    @Override
    public void paintPath(Canvas canvas) {
        if (movementDirection.equals(Direction.CLOCKWISE)) {
            paintPathRightDirection(canvas);
        } else {
            paintPathLetDirection(canvas);
        }
    }

    private void paintPathLetDirection(Canvas canvas) {
        drawWithOffset(zone, backOffset, frontOffset, movementLinePoints, canvas, paint);
    }

    private void paintPathRightDirection(Canvas canvas) {
        drawWithOffset(zone, frontOffset, backOffset, movementLinePoints, canvas, paint);
    }

    @Override
    public void start() {
        movementAnimator.start();
        frontValueAnimator.start();
    }

    @Override
    public void stop() {
    //Empty
    }

    @Override
    public void restart() {
    //Empty
    }

    private class MovementLineAnimatorUpdateListener implements AnimatorValue.ValueUpdateListener {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            zone = currentMoveValue + (endMoveValue - currentMoveValue) * v;
            view.invalidate();
        }
    }

    private class FrontOffsetAnimatorUpdateListener implements AnimatorValue.ValueUpdateListener {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            int points = getPositionForZone(sideIncrementSize);
            frontOffset = (int)(v * points);
        }
    }

    private class FrontOffsetAnimatorListener implements Animator.StateChangedListener {

        private void onAnimationEndRight() {
            zone += sideIncrementSize;
            // 暂未找到替换api
            if (zone < 1) {
                setCurrentPlayTime((long) (zone * movementLoopTime));
            } else {
                setCurrentPlayTime((long) ((zone - 1) * movementLoopTime));
            }
        }

        private void onAnimationEndLeft() {
            zone -= sideIncrementSize;
            //movementAnimator.setDuration((long) ((1 - zone) * movementLoopTime));
            // 暂未找到替换api
            //movementAnimator.setCurrentPlayTime((long) ((1 - zone) * movementLoopTime));
            setCurrentPlayTime((long) ((1 - zone) * movementLoopTime));
        }

        private void setCurrentPlayTime(long time){
            time = time % movementLoopTime;
            float fraction = time * 1.0f / movementLoopTime;
            movementAnimator.stop();
            //movementAnimator.cancel();
            movementAnimator.setDuration(movementLoopTime - time);
            currentMoveValue = endMoveValue == 1 ? fraction : 1 - fraction;
            System.out.println("------ currentMoveValue = " + currentMoveValue + " ," + movementAnimator.getDuration());
            movementAnimator.start();
        }

        @Override
        public void onStart(Animator animator) {
        }

        @Override
        public void onStop(Animator animator) {
        }

        @Override
        public void onCancel(Animator animator) {
        }

        @Override
        public void onEnd(Animator animator) {
            if (movementDirection.equals(Direction.CLOCKWISE)) {
                onAnimationEndRight();
            } else {
                onAnimationEndLeft();
            }
            backOffset = frontOffset;
            frontOffset = 0;
            // 暂未找到替换api
            backValueAnimator.start();
        }

        @Override
        public void onPause(Animator animator) {
      }

    @Override
    public void onResume(Animator animator) {

    }
  }

  private class BackOffsetAnimatorUpdateListener implements AnimatorValue.ValueUpdateListener {
    @Override
    public void onUpdate(AnimatorValue animatorValue, float v) {
      int points = getPositionForZone(sideIncrementSize);
      backOffset = (int) ((1 - v) * points);
    }
  }

  private class BackOffsetAnimatorListener implements Animator.StateChangedListener {

    @Override
    public void onStart(Animator animator) {

    }

    @Override
    public void onStop(Animator animator) {

    }

    @Override
    public void onCancel(Animator animator) {

    }

    @Override
    public void onEnd(Animator animator) {
      backOffset = 0;
      frontOffset = 0;
      frontValueAnimator.start();
    }

    @Override
    public void onPause(Animator animator) {

    }

    @Override
    public void onResume(Animator animator) {

    }
  }
}
